<?php

namespace pay;
use http\Curl;
use think\facade\Log;
use think\facade\Request;

/**
 * Class Wx_Pay
 * @package pay
 */
class Wx extends Api {
    protected $trade_type='JSAPI';
    protected $pay_platform=1;

    /**
     * 签名
     * @param array $postData
     * @return string
     */
    public function sign(array $postData){
        $str = '';
        ksort($postData);
        foreach($postData as $k=>$v){
            if($v!==''){
                $str.= "{$k}={$v}&";
            }
        }
        $str.="key={$this->mchKey}";
        return strtoupper(md5($str));
    }

    /**
     * 退款数据解密
     * @param $str
     * @param $key
     * @return bool|string
     */
    function refund_decrypt($str, $key) {
        $data = base64_decode($str,true);
        return openssl_decrypt($data, 'AES-256-ECB', md5($key), OPENSSL_RAW_DATA);
    }

    /**
     * @param array $params
     * @return mixed
     */
    public function createXMLStr(array $params){
        $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><xml></xml>');
        foreach($params as $k=>$v){
            $xml->addChild($k,$v);
        }
        return $xml->asXML();
    }

    public function getNonceStr($pre = 'JND'){
        return uniqid($pre);
    }

        /**
     * 获取AccessToken
     * @return bool
     */
    public function requestAccessToken() {
        $url = sprintf('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s',
            $this->appId,
            $this->appSecret
        );
        $curl = new Curl();
        $data = $curl->request(
            $url,[]
        );

        if($data['httpcode'] === 200 && isset($data['data']['access_token'])) {
            $this->accessToken = $data['data']['access_token'];
            return true;
        }
        return false;
    }

    /**
     * 获取小程序码
     * @param $params
     * @return bool
     */
    public function requestAcodeUnlimit($params) {
    $url = sprintf('https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s',
        $this->accessToken
    );
    $sendData = json_encode($params,JSON_UNESCAPED_SLASHES);
    $curl = new Curl();
    $curl->setDoLog(false);
    $data = $curl->request(
        $url,$sendData,'POST',false,array(
            'Content-Type: application/json',
            'Content-Length: ' . strlen($sendData)
        )
    );
    //var_dump($data);die;
    if($data['httpcode'] === 200 && strlen($data['data'])>1000) {
        return $data['data'];
    }
    return false;
}

    /**
     * 预支付交易
     * @return bool|string|null
     */
    public function unifiedOrder() {
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';

        $postData = array(
            'appid'=>$this->appId,
            'mch_id'=>$this->mchId,
            'nonce_str'=>$this->getNonceStr(),
            //'sign'=>'',
            'sign_type'=>'MD5',
            'body'=>$this->order_body,
            'out_trade_no'=>$this->order_id,
            'fee_type'=>'CNY',
            'total_fee'=>$this->pay_fee,
            'spbill_create_ip' => $this->client_ip,
            'notify_url' => $this->notify_url,
            'trade_type' => $this->trade_type,
            'openid' => $this->open_id,
            'attach' => $this->attach,
        );

        $postData['sign'] = $this->sign($postData);
        //var_dump($postData)

        $postDataXML = $this->createXMLStr($postData);
        //var_dump($postDataXML);die;

        $curl = new Curl();
        $data = $curl->request(
            $url,$postDataXML,'POST',false,array('Content-Type: text/xml')
        );
        //var_dump($data);die;
        if($data['httpcode'] === 200) {
            $xml = new \SimpleXMLElement($data['data']);
            if(isset($xml->return_code) && $xml->return_code == 'SUCCESS'
                && isset($xml->result_code) && $xml->result_code == 'SUCCESS'){
                $this->prepayId = $xml->prepay_id->__toString();
                return $this->prepayId;
            }
            //var_dump($xml,$xml->asXML());die;
        }
        return false;
    }

    public function getJsApiPayParams(){
        $data = array(
            'timeStamp'=>(string)time(),
            'nonceStr'=>$this->getNonceStr(),
            'package'=>"prepay_id={$this->prepayId}",
            'signType'=>'MD5',
            //'paySign'=>'',
        );
        $signData = $data;
        $signData['appId'] = $this->appId;
        $data['paySign'] = $this->sign($signData);
        //var_dump($data);die;
        return $data;
    }

    /**
     * 支付结果通知验证并处理数据
     * @return bool|mixed
     */
    public function check_notify() {
        $request = file_get_contents("php://input");
        /*$request = '<xml>
  <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
  <attach><![CDATA[支付测试]]></attach>
  <bank_type><![CDATA[CFT]]></bank_type>
  <fee_type><![CDATA[CNY]]></fee_type>
  <is_subscribe><![CDATA[Y]]></is_subscribe>
  <mch_id><![CDATA[1505455621]]></mch_id>
  <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>
  <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
  <out_trade_no><![CDATA[92324109978759]]></out_trade_no>
  <result_code><![CDATA[SUCCESS]]></result_code>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
  <time_end><![CDATA[20140903131540]]></time_end>
  <total_fee>1</total_fee>
<coupon_fee><![CDATA[10]]></coupon_fee>
<coupon_count><![CDATA[1]]></coupon_count>
<coupon_type><![CDATA[CASH]]></coupon_type>
<coupon_id><![CDATA[10000]]></coupon_id>
<coupon_fee><![CDATA[100]]></coupon_fee>
  <trade_type><![CDATA[JSAPI]]></trade_type>
  <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
</xml>';*/
        Log::info("[wx notice] ip=".Request::ip().";data={$request}");
        try {
            $xml = @new \SimpleXMLElement($request);
            if (!isset($xml->return_code) || $xml->return_code != 'SUCCESS'
                || !isset($xml->result_code) || $xml->result_code != 'SUCCESS'
            ) {
                throw new \Exception('参数格式校验错误1');
            }
            $requestData = array();
            foreach ($xml->children() as $k => $v) {
                $requestData[$k] = $v->__toString();
            }
            if (!isset($requestData['out_trade_no']) || !$requestData['out_trade_no']
                || !isset($requestData['sign']) || !$requestData['sign']
            ) {
                throw new \Exception('参数格式校验错误2');
            }
            $signData = $requestData;
            unset($signData['sign']);
            if(strtoupper($requestData['sign']) != $this->sign($signData)){
                throw new \Exception('签名失败');
            }
            $this->setOrderId($requestData['out_trade_no']);
            $this->setPayFee($requestData['total_fee']);
            $this->setTransactionId($requestData['transaction_id']);
            return true;
        } catch (\Exception $e){
            $this->notice_result = false;
            $this->notice_result_msg = $e->getMessage();
            return false;
        }
    }

    /**
     * 申请退款
     * @return bool|string|null  微信退款单号
     */
    public function unifiedOrderRefund(){
        $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';

        $postData = array(
            'appid'=>$this->appId,
            'mch_id'=>$this->mchId,
            'nonce_str'=>$this->getNonceStr(),
            //'sign'=>'',
            'sign_type'=>'MD5',
            'out_trade_no'=>$this->order_id,
            'out_refund_no'=>$this->refund_id,
            'total_fee'=>$this->pay_fee,
            'refund_fee'=>$this->refund_fee,
            'refund_fee_type'=>'CNY',
            'notify_url' => $this->notify_url,
        );

        $postData['sign'] = $this->sign($postData);
        //var_dump($postData)

        $postDataXML = $this->createXMLStr($postData);
        //var_dump($postDataXML);die;

        $curl = new Curl();
        $curl->setSslCert(ROOT_DIR.'/config/wxcert/apiclient_cert.pem');
        $curl->setSslKey(ROOT_DIR.'/config/wxcert/apiclient_key.pem');
        $data = $curl->request(
            $url,$postDataXML,'POST',false,array('Content-Type: text/xml')
        );
        //var_dump($data);die;
        if($data['httpcode'] === 200) {
            $xml = new \SimpleXMLElement($data['data']);
            if(isset($xml->return_code) && $xml->return_code == 'SUCCESS'
                && isset($xml->result_code) && $xml->result_code == 'SUCCESS'){
                return $xml->refund_id->__toString();
            }
            //var_dump($xml,$xml->asXML());die;
        }
        return false;
    }

    /**
     * 退款结果通知验证并处理数据
     * @return bool|mixed
     */
    public function check_refund_notify() {
        $request = file_get_contents("php://input");
        /*$request = '<xml>
<return_code>SUCCESS</return_code>
   <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
   <mch_id><![CDATA[10000100]]></mch_id>
   <nonce_str><![CDATA[TeqClE3i0mvn3DrK]]></nonce_str>
   <req_info><![CDATA[T87GAHG17TGAHG1TGHAHAHA1Y1CIOA9UGJH1GAHV871HAGAGQYQQPOOJMXNBCXBVNMNMAJAA]]></req_info>
</xml>';*/
        Log::info("[wx notice refund] ip=".Request::ip().";data={$request}");
        try {
            $xml = @new \SimpleXMLElement($request);
            if (!isset($xml->return_code) || $xml->return_code != 'SUCCESS'
            ) {
                throw new \Exception('参数格式校验错误1');
            }
            $requestData = array();
            foreach ($xml->children() as $k => $v) {
                $requestData[$k] = $v->__toString();
            }
            if (!isset($requestData['req_info']) || !$requestData['req_info']
            ) {
                throw new \Exception('参数格式校验错误2');
            }
            $reqInfo = $this->refund_decrypt($requestData['req_info'],$this->mchKey);
            Log::info("[wx notice refund decrypt] req_info={$reqInfo}");
            if(!$reqInfo) throw new \Exception('解密错误');
            $infoXml = @new \SimpleXMLElement($reqInfo);
            $refundData = array();
            foreach ($infoXml->children() as $k => $v) {
                $refundData[$k] = $v->__toString();
            }
            if(isset($refundData['refund_status']) && $refundData['refund_status'] == 'SUCCESS') {
                $this->setRefundId($refundData['out_refund_no']);
                $this->setOrderId($refundData['out_trade_no']);
                $this->setRefundFee($refundData['refund_fee']);
                $this->setPayFee($refundData['total_fee']);
            }
            return true;
        } catch (\Exception $e){
            $this->notice_result = false;
            $this->notice_result_msg = $e->getMessage();
            return false;
        }
    }

    /**
     * 通知返回
     * @return string
     */
    public function return_notify() {
        $return = "<xml>
    <return_code><![CDATA[%s]]></return_code>
    <return_msg><![CDATA[%s]]></return_msg>
    </xml>";
        $return_code = $this->notice_result?'SUCCESS':'FAIL';
        Log::info("[wx notice] return_code={$return_code},return_msg={$this->notice_result_msg}");
        echo sprintf($return,$return_code,$this->notice_result_msg);
        exit(0);
    }
}